//! @Author: DengLibin
//! @Date: Create in 2023-10-20 14:11:17
//! @Description: http-client模块 使用reqwest
//!

use std::{
    collections::HashMap,
    path::Path,
    thread,
};

use futures_util::StreamExt;
use reqwest::{
    header::{HeaderMap, HeaderValue},
    Body, Response,
};
use tokio::{
    fs::{self, File},
    io::AsyncWriteExt,
};
use tokio_util::codec::{BytesCodec, FramedRead};

#[derive(Debug, Clone, PartialEq)]
pub struct HttpError {
    msg: String,
}
impl std::fmt::Display for HttpError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "Message: {}", self.msg)
    }
}
// 自定义结果
type HttpResult<T> = std::result::Result<T, HttpError>;

// 客户端
pub struct HttpClient {
    user_agent: &'static str,
    client: reqwest::Client,
}

impl HttpClient {
    pub fn new() -> Self {
        HttpClient {
            user_agent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 Edg/118.0.2088.46",
            client: reqwest::Client::builder()
            .danger_accept_invalid_certs(true)//信任https
            .build().unwrap()
        }
    }

    /// @Author: DengLibin
    /// @Date: Create in 2023-10-20 19:59:44
    /// @Description: 返回错误检查
    pub async fn check_http_result(&self, resp: Response) -> HttpResult<Response> {
        if !resp.status().is_success() {
            let mut s = String::from("访问失败，状态码:");
            s.push_str(resp.status().as_str());
            s.push_str("\n");
            s.push_str("内容:");
            s.push_str(resp.text_with_charset("utf-8").await.unwrap().as_str());
            return Result::Err(HttpError { msg: s });
        }
        return Result::Ok(resp);
    }

    /// @Author: DengLibin
    /// @Date: Create in 2023-10-20 20:15:45
    /// @Description: 结果检查
    pub fn check_result<T, E: std::fmt::Display>(&self, result: Result<T, E>) -> HttpResult<T> {
        return match result {
            Ok(r) => Result::Ok(r),
            Err(err) => Result::Err(HttpError {
                msg: err.to_string(),
            }),
        };
    }

    /// @Author: DengLibin
    /// @Date: Create in 2023-10-23 15:33:00
    /// @Description: 根据文件路径获取文件名
    pub fn get_filename(&self, filepath: &str) -> String {
        let real_path = filepath.replace("\\", "/");
        //获取文件名
        let file_name_option = real_path.rfind("/");
        return match file_name_option {
            Some(idex) => String::from(&real_path[(idex + 1)..]),
            None => real_path,
        };
    }

    /// @Author: DengLibin
    /// @Date: Create in 2023-10-20 14:37:09
    /// @Description: http请求
    pub async fn get(&self, url: &str) -> HttpResult<String> {
        // let r = reqwest::get(url).await;
        // let mut headers = HeaderMap::new();
        // headers.insert("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 Edg/118.0.2088.46".parse().unwrap());
        let r = self
            .client
            .get(url)
            // .headers(headers)
            .header("User-Agent", self.user_agent)
            .send()
            .await;
        let r = self.check_result(r)?;

        let r = self.check_http_result(r).await?;
        let tr: Result<String, reqwest::Error> = r.text_with_charset("utf-8").await;

        let s = self.check_result(tr)?;
        return Result::Ok(s);
    }

    /// @Author: DengLibin
    /// @Date: Create in 2024-03-20 15:15:25
    /// @Description: 下载文件
    pub async fn download_file_header(
        &self,
        url: &str,
        headers: &HashMap<&'static str, String>,
        file_path: &str,
    ) -> HttpResult<()> {
        let mut header_map = HeaderMap::new();
        for (k, v) in headers.into_iter() {
            header_map.insert(*k, HeaderValue::from_str(v).unwrap());
        }

        let r = self
            .client
            .get(url)
            .headers(header_map)
            .header("User-Agent", self.user_agent)
            .send()
            .await;

        let resp = self.check_result(r)?;

        let resp = self.check_http_result(resp).await?;

        //读取所有字节
        let tr = resp.bytes().await;
        let bytes = self.check_result(tr)?;
        //创建文件
        let file_r = File::create(file_path).await;

        let mut file = self.check_result(file_r)?;

        let b = bytes.as_ref();
        //写入文件
        let write_r = file.write_all(b).await;
        let r = self.check_result(write_r)?;
        return Ok(r);
    }
    /// @Author: DengLibin
    /// @Date: Create in 2023-10-20 17:19:53
    /// @Description: 下载文件
    /// @url: url
    /// @file_path: 文件路径
    pub async fn download_file(&self, url: &str, file_path: &str) -> HttpResult<()> {
        let headers: HashMap<&'static str, String> = HashMap::new();
        self.download_file_header(url, &headers, file_path).await
    }

    /// @Author: DengLibin
    /// @Date: Create in 2023-10-20 17:19:53
    /// @Description: 下载文件
    /// @url: url
    /// @file_path: 文件路径
    pub async fn download_by_stream(&self, url: &str, file_path: &str) -> HttpResult<()> {
        let r = self
            .client
            .get(url)
            // .headers(headers)
            .header("User-Agent", self.user_agent)
            .send()
            .await;
        let r = self.check_result(r)?;

        let r = self.check_http_result(r).await?;

        //获取字节流
        let mut stream = r.bytes_stream();

        //创建文件
        let file_r = File::create(file_path).await;
        let mut file = self.check_result(file_r)?;

        //迭代
        while let Some(chunk_result) = stream.next().await {
            let chunk_bytes = self.check_result(chunk_result)?;

            //写入文件
            let write_r = file.write_all(&chunk_bytes).await;

            let _ = self.check_result(write_r)?;
        }

        return Result::Ok(());
    }

    /// @Author: DengLibin
    /// @Date: Create in 2023-10-23 12:28:36
    /// @Description:
    pub async fn post_form(&self, url: &str, params: &[(&str, &str)]) -> HttpResult<String> {
        //    let params = [("foo", "bar"), ("baz", "quux")];
        let r = self
            .client
            .post(url)
            .form(&params)
            // .headers(headers)
            .header("User-Agent", self.user_agent)
            .send()
            .await;
        let r = self.check_result(r)?;

        let r = self.check_http_result(r).await?;
        let tr: Result<String, reqwest::Error> = r.text_with_charset("utf-8").await;

        let s = self.check_result(tr)?;
        return Result::Ok(s);
    }

    /// @Author: DengLibin
    /// @Date: Create in 2023-10-23 13:38:53
    /// @Description:
    pub async fn post_json(&self, url: &str, params: &HashMap<&str, &str>) -> HttpResult<String> {
        let r = self
            .client
            .post(url)
            .json(&params)
            // .headers(headers)
            .header("User-Agent", self.user_agent)
            .send()
            .await;
        let r = self.check_result(r)?;

        let r = self.check_http_result(r).await?;
        let tr: Result<String, reqwest::Error> = r.text_with_charset("utf-8").await;

        let s = self.check_result(tr)?;
        return Result::Ok(s);
    }

    /// @Author: DengLibin
    /// @Date: Create in 2023-10-23 13:53:13
    /// @Description: 上传文件
    pub async fn upload_file(
        &self,
        url: &str,                               //url
        params: &[(&'static str, &'static str)], //文本参数
        file_path: &str,                         //文件路径
    ) -> HttpResult<String> {
        //获取文件名
        let file_name = self.get_filename(file_path);
        let file_r = fs::read(file_path).await;
        let file = self.check_result(file_r)?;

        //文件
        let file_part = reqwest::multipart::Part::bytes(file).file_name(file_name);
        //文件对象
        let mut form = reqwest::multipart::Form::new().part("file", file_part);
        //其他参数
        for item in params {
            form = form.text(item.0, item.1);
        }

        let r = self
            .client
            .post(url)
            .multipart(form)
            // .headers(headers)
            .header("User-Agent", self.user_agent)
            .send()
            .await;
        let r = self.check_result(r)?;

        let r = self.check_http_result(r).await?;
        let tr: Result<String, reqwest::Error> = r.text_with_charset("utf-8").await;

        let s = self.check_result(tr)?;
        return Result::Ok(s);
    }
    /// @Author: DengLibin
    /// @Date: Create in 2023-10-23 15:31:07
    /// @Description:
    pub async fn upload_file_by_stream(
        &self,
        url: &str,                               //url
        params: &[(&'static str, &'static str)], //文本参数
        file_path: impl AsRef<Path>,             //文件路径
    ) -> HttpResult<(String, String)> {
        //获取文件名
        let file_path = file_path.as_ref().as_os_str().to_str();
        let file_path = if let Some(f) = file_path { f } else { "" };
        let file_name = self.get_filename(file_path);
        let mut t_name = "";
        let thread = thread::current();
        if let Some(t) = thread.name() {
            t_name = t;
        }
        println!("[{}]上传文件{}", t_name, file_path);

        let f = self.check_result(File::open(file_path).await)?;
        let stream = FramedRead::new(f, BytesCodec::new());
        //文件流
        let stream_part =
            reqwest::multipart::Part::stream(Body::wrap_stream(stream)).file_name(file_name);
        //文件对象
        let mut form = reqwest::multipart::Form::new().part("file", stream_part);
        //其他参数
        for item in params {
            form = form.text(item.0, item.1);
        }

        let r = self
            .client
            .post(url)
            .multipart(form)
            // .headers(headers)
            .header("User-Agent", self.user_agent)
            .send()
            .await;
        let r = self.check_result(r)?;

        let r = self.check_http_result(r).await?;
        let tr: Result<String, reqwest::Error> = r.text_with_charset("utf-8").await;

        let s = self.check_result(tr)?;
        return Result::Ok((String::from(file_path), s));
    }
}
